//  Brett Jones
//  PBR Senior Thesis
//  Copyright Brett Jones October 2007

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Xml;

namespace PBR
{
    /// <summary>
    /// Creates a bitmap that uses ptrs instead of operations for faster
    /// image manipulation
    /// </summary>
    public class BitmapUnsafe: IDisposable, IConvertToXml
    {
        #region Member Variables

        private Bitmap mBitmap;
        private BitmapData mBitmapData;
        private int depth;
        private string savePath;
        private string name;
        private bool isSaved;
        private bool isLocked = false;

        #endregion

        #region Member Functions

        public BitmapUnsafe(string filename)
        {
            isSaved = false;
            savePath = "";
            mBitmap = new Bitmap(filename);
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
            }
            else
            {
                depth = 4;
            }
        }

        public BitmapUnsafe(string name, string filename)
        {
            isSaved = true;
            this.name = name;
            savePath = "";
            mBitmap = new Bitmap(filename);
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
            }
            else
            {
                depth = 4;
            }
        }

        public BitmapUnsafe(string name, Bitmap toCopy)
        {
            isSaved = true;
            this.name = name;
            savePath = "";
            mBitmap = (Bitmap)toCopy.Clone();
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
            }
            else
            {
                depth = 4;
            }
        }

        public BitmapUnsafe(Bitmap toCopy)
        {
            isSaved = true;
            savePath = "";
            mBitmap = (Bitmap)toCopy.Clone();
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
            }
            else
            {
                depth = 4;
            }
        }

        public BitmapUnsafe(string name, BitmapUnsafe toCopy)
        {
            isSaved = true;
            this.name = name;
            savePath = "";
            mBitmap = new Bitmap(toCopy.Bitmap);
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
            }
            else
            {
                depth = 4;
            }
        }

        public BitmapUnsafe(BitmapUnsafe toCopy)
        {
            isSaved = true;
            savePath = "";
            mBitmap = new Bitmap(toCopy.Bitmap);
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
            }
            else
            {
                depth = 4;
            }
        }

        public BitmapUnsafe(string name, int width, int height, PixelFormat pixelFormat)
        {
            isSaved = true;
            this.name = name;
            savePath = "";
            mBitmap = new Bitmap(width, height, pixelFormat);
            if (mBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                depth = 1;
                // get palette
                ColorPalette cp = mBitmap.Palette;
                // init palette
                for (int i = 0; i < 256; i++)
                {
                    cp.Entries[i] = Color.FromArgb(i, i, i);
                }
                // set palette back
                mBitmap.Palette = cp;
            }
            else
            {
                depth = 4;
            }
        }

        ~BitmapUnsafe()
        {
            mBitmap.Dispose();
        }

        public void ConvertToXml(XmlElement root, XmlDocument doc)
        {
            XmlElement bitmapUnsafe = doc.CreateElement(XMLSchema.BitmapUnsafe);
            bitmapUnsafe.SetAttribute(XMLSchema.Name, name);
            bitmapUnsafe.SetAttribute(XMLSchema.Path, savePath);
            root.AppendChild(bitmapUnsafe);
        }

        public void Save()
        {
            Bitmap.Save(savePath);
        }

        public void Dispose()
        {
            mBitmap.Dispose();
            GC.SuppressFinalize(this);
        }

        public void Lock(ImageLockMode imageLockMode)
        {
            Rectangle fullImage = new Rectangle(0,0,mBitmap.Width,mBitmap.Height);
            mBitmapData = mBitmap.LockBits(fullImage, imageLockMode, mBitmap.PixelFormat);
            
        }

        public void Unlock()
        {
            mBitmap.UnlockBits(mBitmapData);
        }

        public unsafe byte GetCell(int row, int col, int index)
        {
            byte* bitmapDataPtr = (byte*)mBitmapData.Scan0.ToPointer();
            bitmapDataPtr += (mBitmapData.Stride * col) + row * depth;
            return bitmapDataPtr[index];
        }

        public unsafe byte GetCellBW(int row, int col)
        {
            byte* bitmapDataPtr = (byte*)mBitmapData.Scan0;
            bitmapDataPtr += (mBitmapData.Stride * col) + row;
            return *bitmapDataPtr;
        }

        public unsafe byte GetCellAlpha(int row, int col, int index)
        {
            byte* bitmapDataPtr = (byte*)mBitmapData.Scan0;
            bitmapDataPtr += (mBitmapData.Stride * col) + row * depth;
            return (byte) ((((int)bitmapDataPtr[0])*((int)bitmapDataPtr[1]))/255);
        }

        public unsafe void SetCell(byte value, int row, int col, int index)
        {
            byte* bitmapDataPtr = (byte*)mBitmapData.Scan0;
            bitmapDataPtr += (mBitmapData.Stride * col) + row*depth;
            bitmapDataPtr[index] = value;
        }

        public unsafe void SetCellBW(byte value, int row, int col)
        {
            byte* bitmapDataPtr = (byte*)mBitmapData.Scan0;
            bitmapDataPtr += (mBitmapData.Stride * col) + row;
            *bitmapDataPtr = value;
        }

        public unsafe void Clear()
        {
            if (isLocked)
            {
                throw new ArgumentException("Clear called on a locked image");
            }
            
            Lock(ImageLockMode.WriteOnly);

            if (depth == 1)
            {
                for (int row = 0; row < Width; row++)
                {
                    for (int col = 0; col < Height; col++)
                    {
                        SetCellBW(0, row, col);
                    }
                }
            }
            else if (depth == 4)
            {
                for (int row = 0; row < Width; row++)
                {
                    for (int col = 0; col < Height; col++)
                    {
                        SetCell(0, row, col, 0);
                        SetCell(0, row, col, 1);
                        SetCell(0, row, col, 2);
                        SetCell(0, row, col, 3);
                    }
                }
            }


            Unlock();

        }
        #endregion

        #region Properties

        public Bitmap Bitmap
        {
            get
            {
                return mBitmap;
            }
            set
            {
                mBitmap = value;
            }
        }

        public BitmapData BitmapData
        {
            get
            {
                return mBitmapData;
            }
            set
            {
                mBitmapData = value;
            }
        }
        public int Width
        {
            get
            {
                return mBitmap.Width;
            }
        }

        public int Height
        {
            get
            {
                return mBitmap.Height;
            }
        }

        public string SavePath
        {
            get
            {
                return savePath;
            }
            set
            {
                savePath = value;
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }

        }

        //public int Depth
        //{
        //    get
        //    {
        //        return depth;
        //    }
        //    set
        //    {
        //        depth = value;
        //    }
        //}

        public bool Locked
        {
            get
            {
                return isLocked;
            }

        }

        #endregion
    }
}
